package com.hd.assistant.biz.manager.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.hd.assistant.biz.manager.ClickedDataMoveMonitor;
import com.hd.assistant.biz.manager.ClickedRedirectManager;
import com.hd.assistant.dal.dao.ClickedDetailDAO;
import com.hd.assistant.dal.dao.exception.DAOException;
import com.hd.assistant.dal.dataobject.ClickedDetail;



public class DefaultClickedRedirectManager implements ClickedRedirectManager{

	private final static Logger log = LoggerFactory.getLogger(DefaultClickedRedirectManager.class);
	
	private Long writeToDbInterval = 1800000l;//轮训间隔 按毫秒算 具体的时间还要加上随机生成的一段
	/**
	 * 设置本地缓存的最大条数 当达到这个条数时 Monitor会强制触发数据迁移
	 */
	private Long maxLocalSize = 40000l;
	
	private Long monitorLoopInterval = 60*1000l;
	
	private Integer defaultTaskSize = 100;
	/**
	 * 本例为IO密集型 可以设置稍微大一点
	 */
	private Integer TASK_POOP_SIZE = 20;
	
	private ConcurrentHashMap<String, AtomicInteger> datas = null;
	
	private ThreadPoolExecutor threadPool = null;
	
	@Autowired
	ClickedDetailDAO clickedDetailDAO;
	
	
	public Long getWriteToDbInterval() {
		return writeToDbInterval;
	}
	public void setWriteToDbInterval(Long writeToDbInterval) {
		this.writeToDbInterval = writeToDbInterval;
	}
	public void init(){
		log.warn("Begin init DefaultClickedRedirectManager。。。。。。。。");
		//初始化监听器
		ClickedDataMoveMonitor monitor = new ClickedDataMoveMonitor(this);
		Random random = new Random();
		long interval = monitorLoopInterval+random.nextInt(100000);
		log.warn("writeToDbInterval interval is(ms):"+interval);
		monitor.setMonitorLoopInterval(interval);
		monitor.setWriteToDbInterval(writeToDbInterval);
		new Thread(monitor).start();
		
		
		//初始化线程池
		threadPool = new ThreadPoolExecutor(TASK_POOP_SIZE, TASK_POOP_SIZE,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
		
		datas = new ConcurrentHashMap<String, AtomicInteger>();
	}
	public int autoIncrement(String clickedId) {
		try{
			if(datas == null || clickedId == null)
				return -1;
			if(datas.containsKey(clickedId)){
				return datas.get(clickedId).incrementAndGet();
			}else{
				AtomicInteger ato = new AtomicInteger(1);
				datas.put(clickedId, ato);
				return 1;
			}
		}catch(Exception e){
			return -1;//外界调用此方法 必须保证不挂
		}
	}

	/**
	 * 未考虑机子down了数据的丢失问题
	 */
	public boolean dumpToDB() {
		// 获取到需要迁移的数据
		ConcurrentHashMap<String, AtomicInteger> newData= new ConcurrentHashMap<String, AtomicInteger>();
		
		ConcurrentHashMap<String, AtomicInteger> oldData = datas;
		//切换
		datas = newData;
		
		int totalSize = oldData.size();
		int taskSize = totalSize/defaultTaskSize;
		if((totalSize % defaultTaskSize) != 0)
			taskSize = taskSize + 1;
		
		List<ClickedItem> totalItems = new ArrayList<ClickedItem>();
		for(Entry<String, AtomicInteger> entry : oldData.entrySet()){
			String clickedId = entry.getKey();
			int count = entry.getValue().get();
			ClickedItem item = new ClickedItem(clickedId,count);
			totalItems.add(item);
		}
		oldData = null;
		// 分发到线程池
		for(int i=0 ; i<taskSize ;i++){
			int start = i*defaultTaskSize;
			int end = (i+1)*defaultTaskSize > totalItems.size() ? totalItems.size() :(i+1)*defaultTaskSize;
			if(start > totalItems.size()  || end > totalItems.size())
				break;
			
			List<ClickedItem> items = new ArrayList<ClickedItem>(totalItems.subList(start, end));
			MoveToDBTask task = new MoveToDBTask(items);
			this.threadPool.execute(task);
		}
		
		
		return true;
	}
	public boolean needToMove(){
		return this.datas.size() >= maxLocalSize;
	}
	
	protected void convertToDb(ClickedItem item) {	
		String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
		ClickedDetail detail = null;
		try {
			detail = this.clickedDetailDAO.getNearestByClickedId(item.getClickedId());
		} catch (DAOException e) {
			log.error("convertToDb:"+item.getClickedId(),e);
			return;
		}
		if(detail == null || !detail.getClickedDate().equals(date)){
			//表示是当日第一条数据 
			//理论上detail==null的情况不存在 因为分享的时候就会插入数据  暂不处理 后期优化
			if(detail == null){
				return;
			}
			
			detail.setClickedNum(item.getClickedCount());
			detail.setDataType(ClickedDetail.DATA_TYPE_SYS);
			detail.setClickedDate(date);
			try {
				this.clickedDetailDAO.insertClickedDetail(detail);
			} catch (DAOException e) {
				// 其他异常暂时不考虑 如果出现异常 则修改一次 防止 并发出现的两次同时插入被委以键阻止情况
				log.error("convertToDb:",e);
				try {
					this.clickedDetailDAO.updateClickedNumsByItemidAndDate(item.getClickedId(), date, item.getClickedCount());
				} catch (DAOException ee) {
					log.error("convertToDb:",ee);
				}
			}
			
		}else{
			//利用数据库 增加点击次数
			try {
				this.clickedDetailDAO.updateClickedNumsByItemidAndDate(item.getClickedId(), date, item.getClickedCount());
			} catch (DAOException e) {
				log.error("convertToDb:",e);
			}
		}
	}
	
	class MoveToDBTask implements Runnable{

		List<ClickedItem> items;
		public MoveToDBTask(List<ClickedItem> items) {
			super();
			this.items = items;
		}
		public void run() {
			
			for(ClickedItem item : items){
				convertToDb(item);
			}
		}
		
		
	}

	class ClickedItem{
		private String clickedId;
		private Integer clickedCount;
		
		public ClickedItem(String clickedId, Integer clickedCount) {
			super();
			this.clickedId = clickedId;
			this.clickedCount = clickedCount;
		}
		public String getClickedId() {
			return clickedId;
		}
		public void setClickedId(String clickedId) {
			this.clickedId = clickedId;
		}
		public Integer getClickedCount() {
			return clickedCount;
		}
		public void setClickedCount(Integer clickedCount) {
			this.clickedCount = clickedCount;
		}
		
	}

	public Long getMaxLocalSize() {
		return maxLocalSize;
	}
	public void setMaxLocalSize(Long maxLocalSize) {
		this.maxLocalSize = maxLocalSize;
	}
	public Long getMonitorLoopInterval() {
		return monitorLoopInterval;
	}
	public void setMonitorLoopInterval(Long monitorLoopInterval) {
		this.monitorLoopInterval = monitorLoopInterval;
	}
	public Integer getDefaultTaskSize() {
		return defaultTaskSize;
	}
	public void setDefaultTaskSize(Integer defaultTaskSize) {
		this.defaultTaskSize = defaultTaskSize;
	}

}
